package com.gitee.chili.leisure.service.impl;

import com.gitee.chili.leisure.ResponseCode;
import com.gitee.chili.leisure.RoomManager;
import com.gitee.chili.leisure.Tools;
import com.gitee.chili.leisure.domain.*;
import com.gitee.chili.leisure.domain.dto.EntryRoomQuery;
import com.gitee.chili.leisure.enums.Settlement;
import com.gitee.chili.leisure.enums.State;
import com.gitee.chili.leisure.enums.Status;
import com.gitee.chili.leisure.exception.*;
import com.gitee.chili.leisure.game.Player;
import com.gitee.chili.leisure.game.PlayerFactory;
import com.gitee.chili.leisure.game.RoomDetail;
import com.gitee.chili.leisure.repository.RoomRepository;
import com.gitee.chili.leisure.service.*;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * RoomService
 *
 * @author week solider
 * @since 2020.09.04
 */
@Slf4j
@Service
public class RoomService implements IRoomService {

    @Autowired
    private RoomRepository roomRepository;

    @Autowired
    private IGradeService gradeService;

    @Autowired
    private IMemberService memberService;

    @Autowired
    private IInningsService inningsService;

    @Autowired
    private IEntryRoomService entryRoomService;

    @Autowired
    private ICurrencyService currencyService;

    @Override
    public RoomRepository getRepository() {
        return roomRepository;
    }

    @Override
    public RoomDetail createRoom(Room room) {

        if (entryRoomService.isRoomInside(room.getUserId())) {
            throw new InRoomException(ResponseCode.SAT_DOWN);
        }

        // 获取房间等级
        Optional<Grade> gradeOptional = gradeService.findById(room.getGradeId());

        if (gradeOptional.isPresent()) {

            Grade grade = gradeOptional.get();

            // 元转分  分别是房间的最高限制与最低限制
            Long upperScore = Tools.rmbCast(grade.getUpperScore());
            Long lowerScore = Tools.rmbCast(grade.getLowerScore());

            // 获取玩家信息
            Optional<Member> memberOptional = memberService.findById(room.getUserId());

            if (memberOptional.isPresent()) {

                Member member = memberOptional.get();

                // 获取本局游戏支付币种
                Currency pay = inningsService.getPay(room.getInnings());

                Account account = memberService.getAccount(member, pay);

                Assert.notNull(account, "账户余额不足");

                if (negativeEntry(Tools.rmbCast(account.getBalance()), lowerScore, upperScore)) {
                    throw new InsufficientFundsException(ResponseCode.LACK_QUALIFICATION);
                }

                // 账户不可用
                if (Status.isDisabled(account.getStatus())) {
                    throw new AccountLockException("账户已锁定");
                }

                // 进入房间信息
                EntryRoomQuery query = EntryRoomQuery.builder()
                        .gameType(room.getGameType())
                        .roomNum(room.getRoomNum())
                        .memberId(member.getId())
                        .build();

                // 插入进入房间记录
                entryRoom(query, room, member);

                // 构造房间信息
                return RoomDetail.builder()
                        .cappedPoints(grade.getCappedPoints())
                        .createTime(room.getCreateTime())
                        .baseScore(grade.getBaseScore())
                        .peopleNum(room.getPeopleNum())
                        .players(Lists.newArrayList())
                        .roomType(room.getRoomType())
                        .gameType(room.getGameType())
                        .homeowner(room.getUserId())
                        .innings(room.getInnings())
                        .roomNum(room.getRoomNum())
                        .rules(grade.getSetting())
                        .lowerScore(lowerScore)
                        .upperScore(upperScore)
                        .pay(pay.getToken())
                        .build();

            }
        }

        log.error("创建房间失败, 未识别的游戏等级, 等级id: {}", room.getGradeId());
        throw new GameGradeNotFoundException("游戏等级未找到");
    }

    @Override
    public void entryRoom(EntryRoomQuery query, Room room, Member member) {

        // 房间信息
        RoomDetail roomDetail = RoomManager.obtain(query.getRoomNum());

        // 没有获取到缓存的的情况下
        if (Objects.isNull(roomDetail)) {
            throw new RoomDissolvedException(ResponseCode.ROOM_DISSOLVE);
        }

        if (Objects.isNull(room)) {
            room = roomRepository.findByRoomNum(query.getRoomNum());

            if (Objects.isNull(room)) {
                throw new RoomNotFoundException(ResponseCode.NOT_FOUND_ROOM);
            }
        }

        // 房间不可用
        if(!room.getStatus().equals(Status.ENABLE) || !room.getSettlementStatus().equals(Settlement.WAIT)){
            throw new RoomNotAvailableException(ResponseCode.ROOM_NOT_AVAILABLE);
        }

        if (Objects.isNull(member)) {
            Optional<Member> memberOptional = memberService.findById(query.getMemberId());
            if (memberOptional.isPresent()) {
                member = memberOptional.get();
            } else {
                throw new MemberNotFoundException("用户不存在");
            }
        }

        // 当前房间人数
        int currentPeople = roomDetail.getPlayers().size();

        // 防御式判断 为了防止线程问题而发生的人数超载 所以使用 >=
        // 理论上只有 ==
        if (currentPeople >= roomDetail.getPeopleNum()) {
            throw new InRoomException(ResponseCode.ROOM_FULL);
        }

        // 如果已经在某个房间里
        if (entryRoomService.isRoomInside(query.getMemberId())) {
            throw new InRoomException(ResponseCode.SAT_DOWN);
        }

        // 本局支付的游戏币  币种
        Currency currency = currencyService.getCurrency(roomDetail.getPay().getCode());

        // 账户余额
        Account account = memberService.getAccount(member, currency);

        Assert.notNull(account, "资产不足");

        if (negativeEntry(Tools.rmbCast(account.getBalance()), roomDetail.getLowerScore(), roomDetail.getUpperScore())) {
            throw new InsufficientFundsException(ResponseCode.LACK_QUALIFICATION);
        }

        // 插入进场记录
        EntryRoom entryRoom = new EntryRoom()
                .setPortrait(member.getPortrait())
                .setRoomNumber(query.getRoomNum())
                .setCoins(account.getBalance())
                .setPosition(currentPeople)
                .setMember(member)
                .setRoom(room)
                .setStatus(1);          // 是否处于房间内

        entryRoomService.save(entryRoom);

        final Room finalRoom = room;
        final Member finalMember = member;

        // 构造玩家信息 进入房间
        RoomManager.entry(query.getRoomNum(), info ->
                info.getPlayers().add(builderPlayer(finalRoom, finalMember, account.getBalance(), currentPeople)));
    }

    @Override
    public void entryRoom(EntryRoomQuery query) {
        entryRoom(query, null, null);
    }

    @Override
    public void leaveRoom(Long roomNum, Long memberId) {
        entryRoomService.leaveRoom(memberId);
    }

    @Override
    public void dissolved(Long roomNum) {
        Room room = roomRepository.findByRoomNum(roomNum);
        room.setStatus(Status.DISABLE);
        room.setSettlementStatus(Settlement.DISSOLVED);

        List<EntryRoom> records = entryRoomService.queryRoomMember(roomNum);

        // 强制离场
        records.forEach(er -> er.setStatus(0));

        entryRoomService.save(records);
    }

    // ===============================================  私有的 ======================================

    /**
     * 构造玩家信息
     */
    private <T extends Player> T builderPlayer(Room room, Member member, BigDecimal balance, int currentPeople) {

        T player = PlayerFactory.getInstant(room.getGameType());

        player.setGender(member.getGender())
                .setNickname(member.getNickname())
                .setPortrait(member.getPortrait())
                .setGameType(room.getGameType())
                .setRoomNum(room.getRoomNum())
                .setPosition(currentPeople)
                .setUserId(member.getId())
                .setStatus(State.unready)
                .setOnline(Boolean.TRUE)
                .setScore(balance)
                .setIp("unknown");

        return player;
    }

    /**
     * 是否满足进场条件
     *
     * @param balance 玩家余额
     * @param lower   最低余额要求
     * @param upper   最高余额要求
     */
    private boolean negativeEntry(Long balance, Long lower, Long upper) {
        return balance < lower && balance > upper;
    }

}
